Student Debt over Time
#Process the data
sc_time <- read_csv('2010_2019_student_debt.csv')
Error: '2010_2019_student_debt.csv' does not exist in current working directory ('/Users/ConnieXu/Dropbox (Business)/Spring 2021/QMSS 5063 - Data Visualization /Group_G_HigherEd/ideas_drafts').
sum(sc_time$UGDS,na.rm=TRUE)
[1] 90801007
Below is an interactive line graph (2010-2019) which details the trends in student debt over the years.
# CPI Inflation Rates - Got Average Yearly Inflation Rate for Scaling for Student Debt
install.packages('quantmod')
Error in install.packages : Updating loaded packages
library(quantmod)
getSymbols("CPIAUCSL", src='FRED')
[1] "CPIAUCSL"
avg.cpi <- apply.yearly(CPIAUCSL, mean)
cf <- as.data.frame(avg.cpi/as.numeric(avg.cpi['2009']))
cf$Year_Ending <- format(as.Date(row.names(cf), format="%Y-%m-%d"),"%Y")
# Merged for Inflation
sc_time_df <- sc_time %>% group_by(`Year_Ending`) %>% mutate(`Average Annual Student Debt - National` = sum(DEBT_MDN_STUDENT,na.rm=TRUE)/sum(UGDS,na.rm=TRUE)) %>% ungroup() %>%
dplyr::mutate(uni_rank = case_when(
ADM_RATE < 0.2 ~ 'elite/highly selective',
ADM_RATE < 0.3 ~ 'more selective',
ADM_RATE < 0.5 ~ 'selective',
ADM_RATE < 0.7 ~ 'less selective',
TRUE ~ 'not selective')) %>%
mutate(uni_rank = factor(uni_rank, levels=c('not selective', 'less selective', 'selective',
'more selective', 'elite/highly selective'))) %>%
group_by(uni_rank,Year_Ending) %>%
mutate(`Average Annual Student Debt (by Selectivity)` = sum(DEBT_MDN_STUDENT,na.rm=TRUE)/sum(UGDS,na.rm=TRUE)) %>%
ungroup() %>%
group_by(`Year_Ending`,`Average Annual Student Debt (by Selectivity)`,
uni_rank,`Average Annual Student Debt - National`) %>% summarize() %>%
merge(cf) %>%
mutate(`Adjusted Average Annual Student Debt` = `Average Annual Student Debt (by Selectivity)`/
CPIAUCSL) %>%
mutate(`Adjusted Average Annual Student Debt - Composite` = `Average Annual Student Debt - National`/
CPIAUCSL)
`summarise()` regrouping output by 'Year_Ending', 'Average Annual Student Debt (by Selectivity)', 'uni_rank' (override with `.groups` argument)
sc_df <- sc_time_df %>% group_by(`Average Annual Student Debt - National`,`Adjusted Average Annual Student Debt - Composite`,Year_Ending) %>% summarize() %>% mutate(uni_rank='national average') %>% mutate(`Adjusted Average Annual Student Debt`=`Adjusted Average Annual Student Debt - Composite`) %>% dplyr::mutate(`Average Annual Student Debt (by Selectivity)` = `Average Annual Student Debt - National`) %>% merge(cf) %>% select(Year_Ending,`Average Annual Student Debt (by Selectivity)`, uni_rank, `Average Annual Student Debt - National`, CPIAUCSL, `Adjusted Average Annual Student Debt`,`Adjusted Average Annual Student Debt - Composite`)
`summarise()` regrouping output by 'Average Annual Student Debt - National', 'Adjusted Average Annual Student Debt - Composite' (override with `.groups` argument)
sc_time_df <- sc_time_df %>% rbind(sc_df) %>% mutate(uni_rank = factor(uni_rank, levels=c('national average','not selective', 'less selective', 'selective', 'more selective', 'elite/highly selective'))) %>%
mutate(national = ifelse(uni_rank == 'national average', 'y','n'))
sc_df
sc_time_df
brewer.pal(n=10,"PuBuGn")
n too large, allowed maximum for palette PuBuGn is 9
Returning the palette you asked for with that many colors
[1] "#FFF7FB" "#ECE2F0" "#D0D1E6" "#A6BDDB" "#67A9CF" "#3690C0" "#02818A" "#016C59" "#014636"
ShortPuBuGn <- c("#D0D1E6","#A6BDDB","#67A9CF","#3690C0","#02818A")
p <- sc_time_df %>%
ggplot(.,aes(x=Year_Ending,y=`Adjusted Average Annual Student Debt`, color=uni_rank, group=national)) +
geom_point() + geom_line(aes(linetype=national)) +
scale_color_manual(values=c('grey',"#D0D1E6","#A6BDDB","#67A9CF","#3690C0","#02818A"))+
theme(
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.background= element_rect(fill="white")) +
scale_x_continuous(breaks = round(seq(min(sc_time$Year_Ending), max(sc_time$Year_Ending), by = 2),1)) +
labs(x='', y='Median Loan Amount per Student\n(thousands)**',
title='Student Debt Has Been Rising Over The Years',
color='',fill='', caption='**-inflation adjusted')
ggplotly(p)
NA
NA
The following is (instead) a bar graph with fewer ‘university selectivity’ buckets.
Chloropleth showing average student debt (this is only 2019 but I have a filter at the beginning that will allow me to incorporate a slider for the year).
leaflet(states_2019) %>% addProviderTiles("CartoDB.Positron") %>%
addPolygons(fillColor = ~pal(states_2019$`Average Student Loans`),
color = "white",
weight = 0.5,
fillOpacity = 0.7,
highlight = highlightOptions(
weight = 5,
color = "#666",
fillOpacity = 0.7,
bringToFront = TRUE,
),popup=pop_pop) %>%
leaflet::addLegend(position = "bottomleft", pal = pal, values = c(paste('$',round(min(states_2019$`Average Student Loans`))),
paste('$',round(max(states_2019$`Average Student Loans`)))),
title = "Average Student Loans (Per Student)") %>%
leaflet::setView(-98.5795, 39.8282, zoom=3)
Error in paste("$", round(min(states_2019$`Average Student Loans`))) :
object 'states_2019' not found
brewer.pal(n = 8, name = "RdYlGn")
leaflet(sc_time_2019_selective) %>% addTiles('http://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png') %>%
addCircles(col = ~pal1(sc_time_2019_selective$uni_rank),
radius = ~DEBT_MDN,
popup = content,
fillOpacity = 0.7) %>%
leaflet::addLegend(position = "bottomleft",pal = pal1, values = sc_time_2019_selective$uni_rank,
title = "Average Student Loans (Per Student)")
Assuming "long" and "lat" are longitude and latitude, respectively
Data contains 931 rows with either missing or invalid lat/lon values and will be ignored
setwd('~/Dropbox (Business)/Spring 2021/QMSS 5063 - Data Visualization /Group_G_HigherEd/')
The working directory was changed to /Users/ConnieXu/Dropbox (Business)/Spring 2021/QMSS 5063 - Data Visualization /Group_G_HigherEd inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
sc_time <- read_csv('src/2010_2019_student_debt.csv')
Missing column names filled in: 'X1' [1]
── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
.default = col_double(),
INSTNM = col_character(),
CITY = col_character(),
STABBR = col_character(),
ZIP = col_character(),
UNEMP_RATE = col_logical(),
DEBT_MDN = col_character(),
MN_EARN_WNE_P10 = col_logical(),
MD_EARN_WNE_P10 = col_logical(),
School = col_logical(),
State = col_logical(),
True = col_character(),
ADM_RATE_ALL_1 = col_character()
)
ℹ Use `spec()` for the full column specifications.
28660 parsing failures.
row col expected actual file
3223 UNITID a double Community College 'src/2010_2019_student_debt.csv'
3223 School 1/0/T/F/TRUE/FALSE Kenai Peninsula College 'src/2010_2019_student_debt.csv'
3223 State 1/0/T/F/TRUE/FALSE Alaska 'src/2010_2019_student_debt.csv'
3224 UNITID a double Community College 'src/2010_2019_student_debt.csv'
3224 School 1/0/T/F/TRUE/FALSE Kodiak College 'src/2010_2019_student_debt.csv'
.... ...... .................. ....................... ................................
See problems(...) for more details.
sc_time
The code chunk below has not been touched and includes Shiny code for later use.
setwd('~/Dropbox (Business)/Spring 2021/QMSS 5063 - Data Visualization /Group_G_HigherEd/')
The working directory was changed to /Users/ConnieXu/Dropbox (Business)/Spring 2021/QMSS 5063 - Data Visualization /Group_G_HigherEd inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
sc_time <- read_csv('src/2010_2019_student_debt.csv')
Missing column names filled in: 'X1' [1]
── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
.default = col_double(),
INSTNM = col_character(),
CITY = col_character(),
STABBR = col_character(),
ZIP = col_character(),
UNEMP_RATE = col_logical(),
DEBT_MDN = col_character(),
MN_EARN_WNE_P10 = col_logical(),
MD_EARN_WNE_P10 = col_logical(),
School = col_logical(),
State = col_logical(),
True = col_character(),
ADM_RATE_ALL_1 = col_character()
)
ℹ Use `spec()` for the full column specifications.
28660 parsing failures.
row col expected actual file
3223 UNITID a double Community College 'src/2010_2019_student_debt.csv'
3223 School 1/0/T/F/TRUE/FALSE Kenai Peninsula College 'src/2010_2019_student_debt.csv'
3223 State 1/0/T/F/TRUE/FALSE Alaska 'src/2010_2019_student_debt.csv'
3224 UNITID a double Community College 'src/2010_2019_student_debt.csv'
3224 School 1/0/T/F/TRUE/FALSE Kodiak College 'src/2010_2019_student_debt.csv'
.... ...... .................. ....................... ................................
See problems(...) for more details.
library(reshape2)
library(shiny)
ui <- fluidPage(
titlePanel("I love Graphs about Student Debt"),
# CODE BELOW: Add select input named "sex" to choose between "M" and "F"
selectInput('year',
'Year',
choices=c(2010,2011,2012,2013,2014,2015,2016,2017,2018,2019)),
# Add plot output to display top 10 most popular names
leafletOutput("studentdebtmap")
)
server <- function(input, output, session){
# Render plot of top 10 most popular names
sc_time_selective <- reactive({
sc_time %>%
dplyr::filter(Year_Ending == input$year) %>%
dplyr::filter(DEBT_MDN !='PrivacySuppressed') %>%
transform(DEBT_MDN = as.numeric(DEBT_MDN)) %>%
dplyr::mutate(DEBT_MDN = ifelse(is.na(DEBT_MDN), 0, DEBT_MDN)) %>%
mutate(DEBT_MDN_STUDENT = DEBT_MDN*UGDS) %>%
group_by(STABBR) %>%
mutate(`Average Student Loans`=sum(DEBT_MDN_STUDENT,na.rm=TRUE)/sum(UGDS,na.rm=TRUE)) %>%
dplyr::rename(lat = LATITUDE) %>%
dplyr::rename(long = LONGITUDE) %>%
dplyr::rename(state = STABBR) %>%
filter(ADM_RATE < 0.3) %>%
dplyr::mutate(uni_rank = case_when(
ADM_RATE < 0.05 ~ 'elite',
ADM_RATE < 0.2 ~ 'highly selective',
TRUE ~ 'selective'))
})
output$studentdebtmap <- renderLeaflet({
content <- paste(
"School",sc_time_selective$INSTNM,"<br/>",
"Number of Undergrads:",sc_time_selective$UGDS,"<br/>",
"Selectivity:", sc_time_selective$uni_rank,"<br/>",
"Median Debt:",paste('$',round(sc_time_selective$DEBT_MDN,2)),"<br/>",
"Year",sc_time_selective$`Year_Ending`)
leaflet (sc_time) %>% addTiles('http://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png') %>%
addCircles(col = ~pal1(sc_time$uni_rank),
radius = ~DEBT_MDN,
popup = content,
fillOpacity = 0.7) %>%
leaflet::addLegend(position = "bottomleft",pal = pal1, values = sc_time$uni_rank,
title = "Average Student Loans (Per Student)")
})
observe({
leafletProxy("studentdebtmap", data = sc_time_selective())
})
}
shinyApp(ui = ui, server = server)
Listening on http://127.0.0.1:5747
Error in $: object of type 'closure' is not subsettable [No stack trace available]
NA
ui <- fluidPage(
titlePanel('The Cost of Higher Education: An Exploration of Student Debt in American Universities')
)
LS0tCnRpdGxlOiAiVmlzdWFsc19EcmFmdF8wNF8xNCIKYXV0aG9yOiAiQ29ubmllIFh1IgpkYXRlOiAiNC8xNC8yMDIxIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Kc2V0d2QoIn4vRHJvcGJveCAoQnVzaW5lc3MpL1NwcmluZyAyMDIxL1FNU1MgNTA2MyAtIERhdGEgVmlzdWFsaXphdGlvbiAvR3JvdXBfR19IaWdoZXJFZC9zcmMvdmlzdWFscy8iKQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCnNvdXJjZSgib3VydGhlbWUuUiIpCmBgYAoKYGBge3IgcGFja2FnZXMsIGVjaG89RkFMU0UsIGV2YWw9VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KciA9IGdldE9wdGlvbigicmVwb3MiKQpyWyJDUkFOIl0gPSAiaHR0cDovL2NyYW4udXMuci1wcm9qZWN0Lm9yZyIKb3B0aW9ucyhyZXBvcyA9IHIpCiMgaW5zdGFsbC5wYWNrYWdlcyAoYmFzaWMpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShkcGx5cikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeSh0aWR5dmVyc2UpKQoKIyBpbnN0YWxsLnBhY2thZ2VzIChyZWFkaW5nKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoWE1MKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KFJDdXJsKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHJlYWRyKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KCJyZWFkeGwiKSkKCiMgaW5zdGFsbC5wYWNrYWdlcyAodGhlbWVzKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoZ2d0aGVtZXMpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoZ2dyZXBlbCkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShSQ29sb3JCcmV3ZXIpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkodmlyaWRpcykpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShocmJydGhlbWVzKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHBsb3RseSkpCgoKIyBpbnN0YWxsLnBhY2thZ2VzIChtYXBzKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoUmdvb2dsZU1hcHMpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoZ2dtYXApKQpzdXBwcmVzc01lc3NhZ2VzKGluc3RhbGwucGFja2FnZXMoIm1hcHMiKSkKc3VwcHJlc3NNZXNzYWdlcyhpbnN0YWxsLnBhY2thZ2VzKCJ0bWFwIikpICMgaW5zdGFsbCB0aGUgQ1JBTiB2ZXJzaW9uCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeSh0bWFwKSkKc3VwcHJlc3NNZXNzYWdlcyhpbnN0YWxsLnBhY2thZ2VzKCdyZ2VvcycpKQoKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGRldnRvb2xzKSkKIyBMZXQncyBpbnN0YWxsIHRoZSBkZXZlbG9wbWVudCB2ZXJzaW9uIGZyb20gR2l0aHViLiBSdW4KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJyc3R1ZGlvL2xlYWZsZXQiKQpgYGAKCmBgYHtyIGltcG9ydCBnZW5lcmFsIGRhdGEsIGVjaG89VFJVRSwgZXZhbD1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIFNldCB0aGUgV0QgYXMgR3JvdXBfR19IaWdoZXJFZCAKc2V0d2QoIn4vRHJvcGJveCAoQnVzaW5lc3MpL1NwcmluZyAyMDIxL1FNU1MgNTA2MyAtIERhdGEgVmlzdWFsaXphdGlvbiAvR3JvdXBfR19IaWdoZXJFZCIpCgojIFdoaWxlIG91ciBpbml0aWFsIENvbGxlZ2UgU2NvcmVjYXJkIG9ubHkgaW5jbHVkZWQgMjAxOSBpbml0aWFsbHksIEkgd2FzIGFibGUgdG8gcnVuIGEgc2ltcGxlIHB5dGhvbiBzY3JpcHQgdG8gY29uY2F0ZW5hdGUgYW5kIHNlbGVjdCBhIHNtYWxsIG51bWJlciBvZiByZWxldmFudCBjb2x1bW5zIGZvciBvdXIgdmlzdWFsaXphdGlvbiBhbmQgYW5hbHlzaXMuIFRodXMsIGFzIG91ciBmaXJzdCB2aXN1YWxpemF0aW9ucyBvbmx5IHVuY2x1ZGUgMjAxOSwgdGhlIGNvZGUgYmVsb3cgaXMgZm9yIHJlLWZpbHRlcmluZyB0aGUgY29uY2F0ZW5hdGVkIDIwMTAtMjAxOSBkYXRhIGJhY2sgaW50byBzaW1wbHkgMjAxOS4gCgpzY190aW1lIDwtIHJlYWRfY3N2KCdzcmMvMjAxMF8yMDE5X3N0dWRlbnRfZGVidC5jc3YnKSAKc2MgPC0gc2NfdGltZSAlPiUgZmlsdGVyKFllYXJfRW5kaW5nID09IDIwMTkpCgpsaWJyYXJ5KGVkdWNhdGlvbmRhdGEpCiMgVGVzdCBSdW4gd2l0aCB1c2luZyBnZXRfZWR1Y2F0aW9uX2RhdGEgCiMgZGF0YSA8LSBnZXRfZWR1Y2F0aW9uX2RhdGEobGV2ZWwgPSAiY29sbGVnZS11bml2ZXJzaXR5IiwKIyAgICAgc291cmNlID0gImlwZWRzIiwKIyAgICAgdG9waWMgPSAiZGlyZWN0b3J5IiwKIyAgICAgZmlsdGVycyA9IGxpc3QoeWVhciA9IDIwMTkpKQojIGRhdGEKIyBTY29yZWNhcmQgZGF0YSAtIDIwMTkgCgojIyBjaGFuZ2UgcHJvamVjdGlvbiBvZiBzYyBkYXRhCnNjIDwtIHNjICU+JQogIGRwbHlyOjptdXRhdGUodW5pX3JhbmsgPSBjYXNlX3doZW4oCiAgICBBRE1fUkFURSA8IDAuMiB+ICdoaWdobHkgc2VsZWN0aXZlL2VsaXRlJywKICAgIEFETV9SQVRFIDwgMC4zIH4gJ21vcmUgc2VsZWN0aXZlJywKICAgIEFETV9SQVRFIDwgMC41IH4gJ3NlbGVjdGl2ZScsCiAgICBBRE1fUkFURSA8IDAuNyB+ICdsZXNzIHNlbGVjdGl2ZScsCiAgICBUUlVFIH4gJ25vdCBzZWxlY3RpdmUnKSkgJT4lIG11dGF0ZSh1bmlfcmFuayA9IGZhY3Rvcih1bmlfcmFuaywgbGV2ZWxzPWMoJ25vdCBzZWxlY3RpdmUnLCAnbGVzcyBzZWxlY3RpdmUnLCAnc2VsZWN0aXZlJywgJ21vcmUgc2VsZWN0aXZlJywgJ2hpZ2hseSBzZWxlY3RpdmUvZWxpdGUnKSkpCmBgYAoKIyMgU2ltcGxlIFNjYXR0ZXJncmFtIAoKRmlyc3Qgd2UgYXJlIGdvaW5nIHRvIHRyeSB0byBwcmVzZW50IHRoaXMgcGF0dGVybiBmb3IgZGlmZmVyZW50IHRpZXJzIG9mIHVuaXZlcnNpdGllcyAoYWRtaXNzaW9uIHJhdGUgYXMgd2VsbCBhcyBkZWJ0KQpgYGB7ciwgZWNobz1UUlVFLCBldmFsPVRSVUV9CiMgUmVtb3ZlIFByaXZhY3lTdXBwcmVzc2VkIFJlY29yZHMgYW5kIHRyYW5zZm9ybSBEZWJ0IE1lZGlhbiBpbnRvIGEgbnVtZXJpYyB2YWx1ZSAtIHdlIGNhbiBhbHNvIGRvIHRoaXMgb24gdGhlIG1haW4gc2MgZGYKc2MkREVCVF9NRE5baXMubmEoc2MkREVCVF9NRE4pXSA8LSAwOwoKYnJld2VyLnBhbChuPTEwLCJQdUJ1R24iKQpTaG9ydFB1QnVHbiA8LSBjKCIjRDBEMUU2IiwiI0E2QkREQiIsIiM2N0E5Q0YiLCIjMzY5MEMwIiwiIzAyODE4QSIpCgptIDwtIHNjICU+JSBzdWJzZXQoREVCVF9NRE4gIT0nUHJpdmFjeVN1cHByZXNzZWQnKSAlPiUgdHJhbnNmb3JtKERFQlRfTUROID0gYXMubnVtZXJpYyhERUJUX01ETikpICU+JSAKICAgICAgICAgICAgICBnZ3Bsb3QoLiwgYWVzKHg9QURNX1JBVEUsIHk9REVCVF9NRE4sY29sb3I9dW5pX3JhbmspKSArCiAgZ2VvbV9wb2ludChwY2g9MjEpICsKICBnZW9tX3Ntb290aChjb2xvcj0nbmF2eScsIHNlID0gRkFMU0UpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPVNob3J0UHVCdUduKSsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYmFja2dyb3VuZD0gZWxlbWVudF9yZWN0KGZpbGw9IndoaXRlIikpICsKICBzY2FsZV95X2Rpc2NyZXRlKGxpbWl0cz1jKDAsMTAwMDAsMjAwMDAsMzAwMDApLCBsYWJlbHM9YygnMCcsJzEwJywnMjAnLCczMCcpKSArCiAgbGFicyh4PSdBZG1pc3Npb25zIFJhdGUnLCB5PSdNZWRpYW4gTG9hbiBBbW91bnQgcGVyIFN0dWRlbnRcbih0aG91c2FuZHMpJywgCiAgICAgICB0aXRsZT0nU3R1ZGVudCBEZWJ0IGFuZCBBZG1pc3Npb25zIFJhdGUnLAogICAgICAgY29sb3I9J1NlbGVjdGl2aXR5JykKbQpgYGAKClRoaXMgaXMgYSBkYXRhIHRhYmxlIHNob3dpbmcgdGhlIGJyZWFrZG93biBvZiB0aGUgdW5pdmVyc2l0eSByYW5rcyAoYXMgSSBjaG9zZSB0byByYW5rIHRoZW0pLiBJIHdpbGwgbm90ZSB0aGF0IGZyb20gdGhlIGR0IGFsb25lIHd3ZSBhcmUgc2VlaW5nIGEgZG93bndhcmQgdHJlbmQgaW4gdGhlICdNZWRpYW4gU3R1ZGVudCBMb2FucycgY29sdW1uLiAKYGBge3IsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQojIENyZWF0ZSBEYXRhIFRhYmxlIChTdW1tYXJpemVkKSBmb3IgCmxpYnJhcnkoJ3NjYWxlcycpCgpzY19kdCA8LSBzYyAlPiUgc3Vic2V0KERFQlRfTUROICE9J1ByaXZhY3lTdXBwcmVzc2VkJykgJT4lIHRyYW5zZm9ybShERUJUX01ETiA9IGFzLm51bWVyaWMoREVCVF9NRE4pKSAlPiUgZ3JvdXBfYnkodW5pX3JhbmspICU+JSBtdXRhdGUoYE51bWJlciBvZiBVbml2ZXJzaXRpZXNgID0gbigpKSAlPiUgdW5ncm91cCgpICU+JSBtdXRhdGUoREVCVF9NRE5fU1RVREVOVFMgPSBERUJUX01ETipVR0RTKSAlPiUgZ3JvdXBfYnkodW5pX3JhbmspICU+JSBtdXRhdGUoYE1lZGlhbiBTdHVkZW50IExvYW5zYCA9IHBhc3RlKCckJyxyb3VuZChzdW0oREVCVF9NRE5fU1RVREVOVFMsIG5hLnJtPVRSVUUpL3N1bShVR0RTLCBuYS5ybT1UUlVFKSwyKSkpICU+JSAKICBtdXRhdGUoYE1pbiBBY2NlcHRhbmNlIFJhdGVgID0gcGVyY2VudChtaW4oQURNX1JBVEUpKSkgJT4lIG11dGF0ZShgTWF4IEFjY2VwdGFuY2UgUmF0ZWAgPSBwZXJjZW50KG1heChBRE1fUkFURSkpKSAlPiUgdW5ncm91cCgpICU+JSAKICBncm91cF9ieSh1bmlfcmFuayxgTWVkaWFuIFN0dWRlbnQgTG9hbnNgLGBOdW1iZXIgb2YgVW5pdmVyc2l0aWVzYCxgTWluIEFjY2VwdGFuY2UgUmF0ZWAsYE1heCBBY2NlcHRhbmNlIFJhdGVgKSAlPiUgCiAgc3VtbWFyaXplKCkKCmluc3RhbGwucGFja2FnZXMoJ0RUJykKbGlicmFyeShEVCkKdGFibGUgPC0gZGF0YXRhYmxlKHNjX2R0LHN0eWxlID0gImRlZmF1bHQiLGZpbHRlciA9ICd0b3AnLCAgY2FwdGlvbiA9ICdVbml2ZXJzaXRpZXMgYW5kIFNlbGVjdGl2aXR5JykKdGFibGUKCmBgYAoKIyMgU2NhdHRlcmdyYW0gYXMgVmlvbGluIFBsb3QKYGBge3IsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQptIDwtIHNjICU+JSBzdWJzZXQoREVCVF9NRE4gIT0nUHJpdmFjeVN1cHByZXNzZWQnKSAlPiUgdHJhbnNmb3JtKERFQlRfTUROID0gYXMubnVtZXJpYyhERUJUX01ETikpICU+JSAKICBnZ3Bsb3QoLiwgYWVzKHg9dW5pX3JhbmssIHk9REVCVF9NRE4pKSArCiAgZ2VvbV92aW9saW4oYWVzKGZpbGw9dW5pX3JhbmssY29sb3I9dW5pX3JhbmspKSArCiAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4yKSsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9U2hvcnRQdUJ1R24pICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1TaG9ydFB1QnVHbikgKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5iYWNrZ3JvdW5kPSBlbGVtZW50X3JlY3QoZmlsbD0id2hpdGUiKSkrCiAgc2NhbGVfeV9kaXNjcmV0ZShsaW1pdHM9YygwLDEwMDAwLDIwMDAwLDMwMDAwKSwgbGFiZWxzPWMoJzAnLCcxMCcsJzIwJywnMzAnKSkgKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoJ25vdFxuc2VsZWN0aXZlXG4oPjcwJSknLCdsZXNzXG5zZWxlY3RpdmVcbig1MCUtNzAlKScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAnc2VsZWN0aXZlXG4oMzAlLTUwJSknLCdtb3JlXG5zZWxlY3RpdmVcbigyMCUtMzAlKScsJ2hpZ2hseVxuc2VsZWN0aXZlXG4oNSUtMjAlKScsJ2VsaXRlXG4oPDUlKScpKSArCgogIGxhYnMoeD0nU2VsZWN0aXZpdHlcbihhZG1pc3Npb24gcmF0ZSB0aHJlc2hvbGRzKScsIHk9J01lZGlhbiBMb2FuIEFtb3VudCBwZXIgU3R1ZGVudFxuKHRob3VzYW5kcyknLCAKICAgICAgIHRpdGxlPSdTZWxlY3RpdmUgU2Nob29scyBhbmQgU3R1ZGVudCBEZWJ0JywKICAgICAgIGNvbG9yPScnLGZpbGw9JycpCm0KYGBgClNob3dpbmcgdGhlIHByZXZpb3VzIHNjYXR0ZXJncmFtIHNwZWNpZmljYWxseSBhcyB2aW9saW5nIHBsb3RzCgpgYGB7ciwgZWNobz1UUlVFLCBldmFsPVRSVUV9CmlwZWRzMTUgPC0gZ2V0X2VkdWNhdGlvbl9kYXRhKGxldmVsID0gImNvbGxlZ2UtdW5pdmVyc2l0eSIsCiAgICBzb3VyY2UgPSAiaXBlZHMiLAogICAgdG9waWMgPSAiZ3JhZC1yYXRlcy1wZWxsIiwKICAgIGZpbHRlcnMgPSBsaXN0KHllYXIgPSAyMDE1KSkKaXBlZHMxNQpgYGAKCgojIyBTdHVkZW50IERlYnQgb3ZlciBUaW1lCgpgYGB7ciwgZWNobz1UUlVFLCBldmFsPVRSVUV9CgojUHJvY2VzcyB0aGUgZGF0YSAKc2NfdGltZSA8LSByZWFkX2NzdignMjAxMF8yMDE5X3N0dWRlbnRfZGVidC5jc3YnKSAKCnNjX3RpbWU8LSBzY190aW1lICU+JSBzdWJzZXQoREVCVF9NRE4gIT0nUHJpdmFjeVN1cHByZXNzZWQnKSAlPiUgCiAgdHJhbnNmb3JtKERFQlRfTUROID0gYXMubnVtZXJpYyhERUJUX01ETikpICU+JSAKICBkcGx5cjo6bXV0YXRlKERFQlRfTUROID0gaWZlbHNlKGlzLm5hKERFQlRfTUROKSwgMCwgREVCVF9NRE4pKQoKYGBgCgoKYGBge3IsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQojUHJvY2VzcyB0aGUgZGF0YSAKc2NfdGltZSA8LSByZWFkX2NzdignMjAwOV8yMDE5X3N0dWRlbnRfZGVidC5jc3YnKSAKCnNjX3RpbWU8LSBzY190aW1lICU+JSBzdWJzZXQoREVCVF9NRE4gIT0nUHJpdmFjeVN1cHByZXNzZWQnKSAlPiUgCiAgdHJhbnNmb3JtKERFQlRfTUROID0gYXMubnVtZXJpYyhERUJUX01ETikpICU+JSAKICBkcGx5cjo6bXV0YXRlKERFQlRfTUROID0gaWZlbHNlKGlzLm5hKERFQlRfTUROKSwgMCwgREVCVF9NRE4pKSAlPiUgCiAgbXV0YXRlKERFQlRfTUROX1NUVURFTlQgPSBERUJUX01ETipVR0RTKQpzY190aW1lCgpzdW0oc2NfdGltZSRVR0RTLG5hLnJtPVRSVUUpCmBgYAoKQmVsb3cgaXMgYW4gaW50ZXJhY3RpdmUgbGluZSBncmFwaCAoMjAxMC0yMDE5KSB3aGljaCBkZXRhaWxzIHRoZSB0cmVuZHMgaW4gc3R1ZGVudCBkZWJ0IG92ZXIgdGhlIHllYXJzLiAKYGBge3IsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQojIENQSSBJbmZsYXRpb24gUmF0ZXMgLSBHb3QgQXZlcmFnZSBZZWFybHkgSW5mbGF0aW9uIFJhdGUgZm9yIFNjYWxpbmcgZm9yIFN0dWRlbnQgRGVidCAKaW5zdGFsbC5wYWNrYWdlcygncXVhbnRtb2QnKQpsaWJyYXJ5KHF1YW50bW9kKQpnZXRTeW1ib2xzKCJDUElBVUNTTCIsIHNyYz0nRlJFRCcpCmF2Zy5jcGkgPC0gYXBwbHkueWVhcmx5KENQSUFVQ1NMLCBtZWFuKQpjZiA8LSBhcy5kYXRhLmZyYW1lKGF2Zy5jcGkvYXMubnVtZXJpYyhhdmcuY3BpWycyMDA5J10pKSAKY2YkWWVhcl9FbmRpbmcgPC0gZm9ybWF0KGFzLkRhdGUocm93Lm5hbWVzKGNmKSwgZm9ybWF0PSIlWS0lbS0lZCIpLCIlWSIpCgojIE1lcmdlZCBmb3IgSW5mbGF0aW9uIApzY190aW1lX2RmIDwtIHNjX3RpbWUgJT4lIGdyb3VwX2J5KGBZZWFyX0VuZGluZ2ApICU+JSBtdXRhdGUoYEF2ZXJhZ2UgQW5udWFsIFN0dWRlbnQgRGVidCAtIE5hdGlvbmFsYCA9IHN1bShERUJUX01ETl9TVFVERU5ULG5hLnJtPVRSVUUpL3N1bShVR0RTLG5hLnJtPVRSVUUpKSAlPiUgdW5ncm91cCgpICU+JSAKICBkcGx5cjo6bXV0YXRlKHVuaV9yYW5rID0gY2FzZV93aGVuKAogICAgQURNX1JBVEUgPCAwLjIgfiAnZWxpdGUvaGlnaGx5IHNlbGVjdGl2ZScsCiAgICBBRE1fUkFURSA8IDAuMyB+ICdtb3JlIHNlbGVjdGl2ZScsCiAgICBBRE1fUkFURSA8IDAuNSB+ICdzZWxlY3RpdmUnLAogICAgQURNX1JBVEUgPCAwLjcgfiAnbGVzcyBzZWxlY3RpdmUnLAogICAgVFJVRSB+ICdub3Qgc2VsZWN0aXZlJykpICU+JQogIG11dGF0ZSh1bmlfcmFuayA9IGZhY3Rvcih1bmlfcmFuaywgbGV2ZWxzPWMoJ25vdCBzZWxlY3RpdmUnLCAnbGVzcyBzZWxlY3RpdmUnLCAnc2VsZWN0aXZlJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnbW9yZSBzZWxlY3RpdmUnLCAnZWxpdGUvaGlnaGx5IHNlbGVjdGl2ZScpKSkgJT4lCiAgZ3JvdXBfYnkodW5pX3JhbmssWWVhcl9FbmRpbmcpICU+JSAKICBtdXRhdGUoYEF2ZXJhZ2UgQW5udWFsIFN0dWRlbnQgRGVidCAoYnkgU2VsZWN0aXZpdHkpYCA9IHN1bShERUJUX01ETl9TVFVERU5ULG5hLnJtPVRSVUUpL3N1bShVR0RTLG5hLnJtPVRSVUUpKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBncm91cF9ieShgWWVhcl9FbmRpbmdgLGBBdmVyYWdlIEFubnVhbCBTdHVkZW50IERlYnQgKGJ5IFNlbGVjdGl2aXR5KWAsCiAgICAgICAgICAgdW5pX3JhbmssYEF2ZXJhZ2UgQW5udWFsIFN0dWRlbnQgRGVidCAtIE5hdGlvbmFsYCkgJT4lIHN1bW1hcml6ZSgpICU+JSAKICBtZXJnZShjZikgJT4lIAogIG11dGF0ZShgQWRqdXN0ZWQgQXZlcmFnZSBBbm51YWwgU3R1ZGVudCBEZWJ0YCA9IGBBdmVyYWdlIEFubnVhbCBTdHVkZW50IERlYnQgKGJ5IFNlbGVjdGl2aXR5KWAvCiAgICAgICAgICAgQ1BJQVVDU0wpICU+JSAKICBtdXRhdGUoYEFkanVzdGVkIEF2ZXJhZ2UgQW5udWFsIFN0dWRlbnQgRGVidCAtIENvbXBvc2l0ZWAgPSBgQXZlcmFnZSBBbm51YWwgU3R1ZGVudCBEZWJ0IC0gTmF0aW9uYWxgLwogICAgICAgICAgIENQSUFVQ1NMKQoKc2NfZGYgPC0gc2NfdGltZV9kZiAlPiUgZ3JvdXBfYnkoYEF2ZXJhZ2UgQW5udWFsIFN0dWRlbnQgRGVidCAtIE5hdGlvbmFsYCxgQWRqdXN0ZWQgQXZlcmFnZSBBbm51YWwgU3R1ZGVudCBEZWJ0IC0gQ29tcG9zaXRlYCxZZWFyX0VuZGluZykgJT4lIHN1bW1hcml6ZSgpICU+JSBtdXRhdGUodW5pX3Jhbms9J25hdGlvbmFsIGF2ZXJhZ2UnKSAlPiUgbXV0YXRlKGBBZGp1c3RlZCBBdmVyYWdlIEFubnVhbCBTdHVkZW50IERlYnRgPWBBZGp1c3RlZCBBdmVyYWdlIEFubnVhbCBTdHVkZW50IERlYnQgLSBDb21wb3NpdGVgKSAlPiUgZHBseXI6Om11dGF0ZShgQXZlcmFnZSBBbm51YWwgU3R1ZGVudCBEZWJ0IChieSBTZWxlY3Rpdml0eSlgID0gYEF2ZXJhZ2UgQW5udWFsIFN0dWRlbnQgRGVidCAtIE5hdGlvbmFsYCkgJT4lIG1lcmdlKGNmKSAlPiUgc2VsZWN0KFllYXJfRW5kaW5nLGBBdmVyYWdlIEFubnVhbCBTdHVkZW50IERlYnQgKGJ5IFNlbGVjdGl2aXR5KWAsIHVuaV9yYW5rLCBgQXZlcmFnZSBBbm51YWwgU3R1ZGVudCBEZWJ0IC0gTmF0aW9uYWxgLCBDUElBVUNTTCwgYEFkanVzdGVkIEF2ZXJhZ2UgQW5udWFsIFN0dWRlbnQgRGVidGAsYEFkanVzdGVkIEF2ZXJhZ2UgQW5udWFsIFN0dWRlbnQgRGVidCAtIENvbXBvc2l0ZWApCnNjX3RpbWVfZGYgPC0gc2NfdGltZV9kZiAlPiUgcmJpbmQoc2NfZGYpICU+JSBtdXRhdGUodW5pX3JhbmsgPSBmYWN0b3IodW5pX3JhbmssIGxldmVscz1jKCduYXRpb25hbCBhdmVyYWdlJywnbm90IHNlbGVjdGl2ZScsICdsZXNzIHNlbGVjdGl2ZScsICdzZWxlY3RpdmUnLCAnbW9yZSBzZWxlY3RpdmUnLCAnZWxpdGUvaGlnaGx5IHNlbGVjdGl2ZScpKSkgJT4lIAogIG11dGF0ZShuYXRpb25hbCA9IGlmZWxzZSh1bmlfcmFuayA9PSAnbmF0aW9uYWwgYXZlcmFnZScsICd5JywnbicpKQpzY19kZgpzY190aW1lX2RmCgoKcCA8LSBzY190aW1lX2RmICU+JSAKICBnZ3Bsb3QoLixhZXMoeD1ZZWFyX0VuZGluZyx5PWBBZGp1c3RlZCBBdmVyYWdlIEFubnVhbCBTdHVkZW50IERlYnRgLCBjb2xvcj11bmlfcmFuaywgZ3JvdXA9bmF0aW9uYWwpKSArIAogIGdlb21fcG9pbnQoKSArIGdlb21fbGluZShhZXMobGluZXR5cGU9bmF0aW9uYWwpKSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygnZ3JleScsIiNEMEQxRTYiLCIjQTZCRERCIiwiIzY3QTlDRiIsIiMzNjkwQzAiLCIjMDI4MThBIikpKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5iYWNrZ3JvdW5kPSBlbGVtZW50X3JlY3QoZmlsbD0id2hpdGUiKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSByb3VuZChzZXEobWluKHNjX3RpbWUkWWVhcl9FbmRpbmcpLCBtYXgoc2NfdGltZSRZZWFyX0VuZGluZyksIGJ5ID0gMiksMSkpICsKICBsYWJzKHg9JycsIHk9J0luZmxhdGlvbi1BZGp1c3RlZCBNZWRpYW4gTG9hbiBBbW91bnQgcGVyIFN0dWRlbnRcbih0aG91c2FuZHMpJywgCiAgICAgICB0aXRsZT0nU3R1ZGVudCBEZWJ0IEhhcyBCZWVuIFJpc2luZyBPdmVyIFRoZSBZZWFycycsCiAgICAgICBjb2xvcj0nJyxmaWxsPScnLGdyb3VwPScnKQpnZ3Bsb3RseShwKQoKCmBgYAoKVGhlIGZvbGxvd2luZyBpcyAoaW5zdGVhZCkgYSBiYXIgZ3JhcGggd2l0aCBmZXdlciAndW5pdmVyc2l0eSBzZWxlY3Rpdml0eScgYnVja2V0cy4gCmBgYHtyfQpzY190aW1lX2RmIDwtIHNjX3RpbWUgJT4lIGdyb3VwX2J5KGBZZWFyX0VuZGluZ2ApICU+JSBtdXRhdGUoYEF2ZXJhZ2UgQW5udWFsIFN0dWRlbnQgRGVidCAtIE5hdGlvbmFsYCA9IHN1bShERUJUX01ETl9TVFVERU5ULG5hLnJtPVRSVUUpL3N1bShVR0RTLG5hLnJtPVRSVUUpKSAlPiUgdW5ncm91cCgpICU+JSAKICBkcGx5cjo6bXV0YXRlKHVuaV9yYW5rID0gY2FzZV93aGVuKAogICAgQURNX1JBVEUgPCAwLjIgfiAnaGlnaGx5IHNlbGVjdGl2ZScsCiAgICBBRE1fUkFURSA8IDAuNSB+ICdtb2RlcmF0ZWx5IHNlbGVjdGl2ZScsCiAgICBUUlVFIH4gJ2xlc3Mvbm90IGF0IGFsbCBzZWxlY3RpdmUnKSkgJT4lCiAgbXV0YXRlKHVuaV9yYW5rID0gZmFjdG9yKHVuaV9yYW5rLCBsZXZlbHM9YygnbGVzcy9ub3QgYXQgYWxsIHNlbGVjdGl2ZScsICdtb2RlcmF0ZWx5IHNlbGVjdGl2ZScsICdoaWdobHkgc2VsZWN0aXZlJykpKSAlPiUKICBncm91cF9ieSh1bmlfcmFuayxZZWFyX0VuZGluZykgJT4lIAogIG11dGF0ZShgQXZlcmFnZSBBbm51YWwgU3R1ZGVudCBEZWJ0IChieSBTZWxlY3Rpdml0eSlgID0gc3VtKERFQlRfTUROX1NUVURFTlQsbmEucm09VFJVRSkvc3VtKFVHRFMsbmEucm09VFJVRSkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGdyb3VwX2J5KGBZZWFyX0VuZGluZ2AsYEF2ZXJhZ2UgQW5udWFsIFN0dWRlbnQgRGVidCAoYnkgU2VsZWN0aXZpdHkpYCwKICAgICAgICAgICB1bmlfcmFuayxgQXZlcmFnZSBBbm51YWwgU3R1ZGVudCBEZWJ0IC0gTmF0aW9uYWxgKSAlPiUgc3VtbWFyaXplKCkgJT4lIAogIG1lcmdlKGNmKSAlPiUgCiAgbXV0YXRlKGBBZGp1c3RlZCBBdmVyYWdlIEFubnVhbCBTdHVkZW50IERlYnRgID0gYEF2ZXJhZ2UgQW5udWFsIFN0dWRlbnQgRGVidCAoYnkgU2VsZWN0aXZpdHkpYC8KICAgICAgICAgICBDUElBVUNTTCkgJT4lIAogIG11dGF0ZShgQWRqdXN0ZWQgQXZlcmFnZSBBbm51YWwgU3R1ZGVudCBEZWJ0IC0gQ29tcG9zaXRlYCA9IGBBdmVyYWdlIEFubnVhbCBTdHVkZW50IERlYnQgLSBOYXRpb25hbGAvCiAgICAgICAgICAgQ1BJQVVDU0wpCgpzY19kZiA8LSBzY190aW1lX2RmICU+JSBncm91cF9ieShgQXZlcmFnZSBBbm51YWwgU3R1ZGVudCBEZWJ0IC0gTmF0aW9uYWxgLGBBZGp1c3RlZCBBdmVyYWdlIEFubnVhbCBTdHVkZW50IERlYnQgLSBDb21wb3NpdGVgLFllYXJfRW5kaW5nKSAlPiUgc3VtbWFyaXplKCkgJT4lIG11dGF0ZSh1bmlfcmFuaz0nbmF0aW9uYWwgYXZlcmFnZScpICU+JSBtdXRhdGUoYEFkanVzdGVkIEF2ZXJhZ2UgQW5udWFsIFN0dWRlbnQgRGVidGA9YEFkanVzdGVkIEF2ZXJhZ2UgQW5udWFsIFN0dWRlbnQgRGVidCAtIENvbXBvc2l0ZWApICU+JSBkcGx5cjo6bXV0YXRlKGBBdmVyYWdlIEFubnVhbCBTdHVkZW50IERlYnQgKGJ5IFNlbGVjdGl2aXR5KWAgPSBgQXZlcmFnZSBBbm51YWwgU3R1ZGVudCBEZWJ0IC0gTmF0aW9uYWxgKSAlPiUgbWVyZ2UoY2YpICU+JSBzZWxlY3QoWWVhcl9FbmRpbmcsYEF2ZXJhZ2UgQW5udWFsIFN0dWRlbnQgRGVidCAoYnkgU2VsZWN0aXZpdHkpYCwgdW5pX3JhbmssIGBBdmVyYWdlIEFubnVhbCBTdHVkZW50IERlYnQgLSBOYXRpb25hbGAsIENQSUFVQ1NMLCBgQWRqdXN0ZWQgQXZlcmFnZSBBbm51YWwgU3R1ZGVudCBEZWJ0YCxgQWRqdXN0ZWQgQXZlcmFnZSBBbm51YWwgU3R1ZGVudCBEZWJ0IC0gQ29tcG9zaXRlYCkKc2NfdGltZV9kZiA8LSBzY190aW1lX2RmICU+JSByYmluZChzY19kZikgJT4lIG11dGF0ZSh1bmlfcmFuayA9IGZhY3Rvcih1bmlfcmFuaywgbGV2ZWxzPWMoJ25hdGlvbmFsIGF2ZXJhZ2UnLCdsZXNzL25vdCBhdCBhbGwgc2VsZWN0aXZlJywgJ21vZGVyYXRlbHkgc2VsZWN0aXZlJywgJ2hpZ2hseSBzZWxlY3RpdmUnKSkpIAoKZmlnMSA8LSBzY190aW1lX2RmICU+JSBwbG90X2x5KHggPSB+WWVhcl9FbmRpbmcsIHkgPSB+YEFkanVzdGVkIEF2ZXJhZ2UgQW5udWFsIFN0dWRlbnQgRGVidGAsIHR5cGUgPSAnYmFyJywKICBjb2xvciA9IH51bmlfcmFuaywgYWxwaGE9MC44LCBob3ZlcnRlbXBsYXRlID0gJ0F2ZXJhZ2UgRGVidC9TdHVkZW50IChVU0QpOiAle3l9IDxleHRyYT48L2V4dHJhPicsY29sb3JzPSdQdXJwbGVzJykgJT4lIAogIGxheW91dCh5YXhpcyA9IGxpc3QoCiAgdGl0bGUgPSAiQXZlcmFnZSBBbm51YWwgU3R1ZGVudCBEZWJ0IHBlciBTdHVkZW50XG4oQWRqdXN0ZWQgZm9yIEluZmxhdGlvbikiKSkKCmZpZzEKCmBgYAoKQ2hsb3JvcGxldGggc2hvd2luZyBhdmVyYWdlIHN0dWRlbnQgZGVidCAodGhpcyBpcyBvbmx5IDIwMTkgYnV0IEkgaGF2ZSBhIGZpbHRlciBhdCB0aGUgYmVnaW5uaW5nIHRoYXQgd2lsbCBhbGxvdyBtZSB0byBpbmNvcnBvcmF0ZSBhIHNsaWRlciBmb3IgdGhlIHllYXIpLgoKYGBge3IgU3R1ZGVudCBEZWJ0IG1hcCAtIENobG9yb3BsZXRoLCBlY2hvPVRSVUUsIGV2YWw9VFJVRSB9CiMgQWRkaXRpb25zIG9mIFN0YXRlcyBkZiBmcm9tIFRpZ3JpcyBGaWxlIApsaWJyYXJ5KHRpZ3JpcykKc3RhdGVzIDwtIHN0YXRlcyhjYiA9IFRSVUUpCgojIENhbiBjaGFuZ2UgdG8gc2NfdGltZV95ZWFyIApzY190aW1lXzIwMTkgPC0gc2NfdGltZSAlPiUgc3Vic2V0KFllYXJfRW5kaW5nID0gMjAxOSkgJT4lIAogIGdyb3VwX2J5KFNUQUJCUikgJT4lIG11dGF0ZShgQXZlcmFnZSBTdHVkZW50IExvYW5zYD1zdW0oREVCVF9NRE5fU1RVREVOVCxuYS5ybT1UUlVFKS9zdW0oVUdEUyxuYS5ybT1UUlVFKSkKc2NfdGltZV8yMDE5X3N0YXRlIDwtIHNjX3RpbWVfMjAxOSAlPiUgZ3JvdXBfYnkoU1RBQkJSLGBBdmVyYWdlIFN0dWRlbnQgTG9hbnNgKSAlPiUgc3VtbWFyaXplKCkKCiMgU3RhdGVzIApzdGF0ZXNfMjAxOSA8LSBzdGF0ZXMgJT4lIAogIGlubmVyX2pvaW4oc2NfdGltZV8yMDE5X3N0YXRlLCBieT1jKFNUVVNQUz0nU1RBQkJSJykpIAoKCmxpYnJhcnkobGVhZmxldC5wcm92aWRlcnMpCmxpYnJhcnkobGVhZmxldCkKI3VzZWQgJ3N1Y2Nlc3MnIG1lYXN1cmVzLiAKCnBhbCA9IGNvbG9yRmFjdG9yKFNob3J0UHVCdUduLCBkb21haW4gPSBzdGF0ZXNfMjAxOSRgQXZlcmFnZSBTdHVkZW50IExvYW5zYCkKc3RhdGVzXzIwMTkKcG9wX3BvcCA8LSBwYXN0ZSgiU3RhdGU6IixzdGF0ZXNfMjAxOSROQU1FLCI8YnIvPiIsCiAgICAgICAgICAgICAgICAgIkF2ZXJhZ2UgU3R1ZGVudCBMb2FucyIsIjxici8+IiwKICAgICAgICAgICAgICAgICAib2YgU2Nob29scyBMb2NhdGVkIGluIFN0YXRlOiIscGFzdGUoJyQnLHJvdW5kKHN0YXRlc18yMDE5JGBBdmVyYWdlIFN0dWRlbnQgTG9hbnNgKSkpCgpsZWFmbGV0KHN0YXRlc18yMDE5KSAlPiUgYWRkUHJvdmlkZXJUaWxlcygiQ2FydG9EQi5Qb3NpdHJvbiIpICU+JQogIGFkZFBvbHlnb25zKGZpbGxDb2xvciA9IH5wYWwoc3RhdGVzXzIwMTkkYEF2ZXJhZ2UgU3R1ZGVudCBMb2Fuc2ApLAogICAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIiwKICAgICAgICAgICAgICB3ZWlnaHQgPSAwLjUsCiAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjcsICAKICAgICAgICAgICAgICBoaWdobGlnaHQgPSBoaWdobGlnaHRPcHRpb25zKAogICAgICAgICAgICAgICAgd2VpZ2h0ID0gNSwKICAgICAgICAgICAgICAgIGNvbG9yID0gIiM2NjYiLAogICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjcsCiAgICAgICAgICAgICAgICBicmluZ1RvRnJvbnQgPSBUUlVFLAogICAgICAgICAgICAgICAgKSxwb3B1cD1wb3BfcG9wKSAlPiUKICBsZWFmbGV0OjphZGRMZWdlbmQocG9zaXRpb24gPSAiYm90dG9tbGVmdCIsIHBhbCA9IHBhbCwgdmFsdWVzID0gYyhwYXN0ZSgnJCcscm91bmQobWluKHN0YXRlc18yMDE5JGBBdmVyYWdlIFN0dWRlbnQgTG9hbnNgKSkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKCckJyxyb3VuZChtYXgoc3RhdGVzXzIwMTkkYEF2ZXJhZ2UgU3R1ZGVudCBMb2Fuc2ApKSkpLAogICAgICAgICAgICB0aXRsZSA9ICJBdmVyYWdlIFN0dWRlbnQgTG9hbnMgKFBlciBTdHVkZW50KSIpICU+JQogIGxlYWZsZXQ6OnNldFZpZXcoLTk4LjU3OTUsIDM5LjgyODIsIHpvb209MykKP2FkZExlZ2VuZApgYGAKCmBgYHtyfQpicmV3ZXIucGFsKG4gPSA4LCBuYW1lID0gIlJkWWxHbiIpCmBgYAoKYGBge3IgU3R1ZGVudCBEZWJ0IE1hcCAtIHBvaW50cywgZWNobz1UUlVFLCBldmFsPVRSVUUgfQojIGxhc3QgZWRpdCB0byBzY190aW1lXzIwMTkgaW4gY2h1bmsgYWJvdmUgZm9yIGNobG9yb3BsZXRoCnNjX3RpbWVfMjAxOV9zZWxlY3RpdmUgPC0gc2NfdGltZV8yMDE5ICU+JSBkcGx5cjo6cmVuYW1lKGxhdCA9IExBVElUVURFKSAlPiUgZHBseXI6OnJlbmFtZShsb25nID0gTE9OR0lUVURFKSAlPiUgCiAgZHBseXI6OnJlbmFtZShzdGF0ZSA9IFNUQUJCUikgJT4lIGZpbHRlcihBRE1fUkFURSA8IDAuMykgJT4lIAogIGRwbHlyOjptdXRhdGUodW5pX3JhbmsgPSBjYXNlX3doZW4oCiAgICBBRE1fUkFURSA8IDAuMDUgfiAnZWxpdGUnLAogICAgQURNX1JBVEUgPCAwLjIgfiAnaGlnaGx5IHNlbGVjdGl2ZScsCiAgICBUUlVFIH4gJ3NlbGVjdGl2ZScpKQpzY190aW1lXzIwMTlfc2VsZWN0aXZlIDwtIHNjX3RpbWVfMjAxOV9zZWxlY3RpdmUgJT4lIHN1YnNldChERUJUX01ETiAhPSdQcml2YWN5U3VwcHJlc3NlZCcpICU+JSAKICB0cmFuc2Zvcm0oREVCVF9NRE4gPSBhcy5udW1lcmljKERFQlRfTUROKSkgJT4lIAogIGRwbHlyOjptdXRhdGUoREVCVF9NRE4gPSBpZmVsc2UoaXMubmEoREVCVF9NRE4pLCAwLCBERUJUX01ETikpIAoKCgpwYWwxID0gY29sb3JGYWN0b3IoU2hvcnRQdUJ1R24sIGRvbWFpbiA9IHNjX3RpbWVfMjAxOV9zZWxlY3RpdmUkYHVuaV9yYW5rYCxyZXZlcnNlPVRSVUUpCgojc2V0IHBvcHVwcyAKY29udGVudCA8LSBwYXN0ZSgiU2Nob29sIixzY190aW1lXzIwMTlfc2VsZWN0aXZlJElOU1ROTSwiPGJyLz4iLAogICAgICAgICAgICAgICAgICJOdW1iZXIgb2YgVW5kZXJncmFkczoiLHNjX3RpbWVfMjAxOV9zZWxlY3RpdmUkVUdEUywiPGJyLz4iLAogICAgICAgICAgICAgICAgICJTZWxlY3Rpdml0eToiLCBzY190aW1lXzIwMTlfc2VsZWN0aXZlJHVuaV9yYW5rLCI8YnIvPiIsCiAgICAgICAgICAgICAgICAgIk1lZGlhbiBEZWJ0OiIscGFzdGUoJyQnLHJvdW5kKHNjX3RpbWVfMjAxOV9zZWxlY3RpdmUkREVCVF9NRE4sMikpLCI8YnIvPiIpCnNjX3RpbWVfMjAxOV9zZWxlY3RpdmUKbGVhZmxldChzY190aW1lXzIwMTlfc2VsZWN0aXZlKSAlPiUgYWRkVGlsZXMoJ2h0dHA6Ly97c30uYmFzZW1hcHMuY2FydG9jZG4uY29tL2RhcmtfYWxsL3t6fS97eH0ve3l9LnBuZycpICU+JQogIGFkZENpcmNsZXMoY29sID0gfnBhbDEoc2NfdGltZV8yMDE5X3NlbGVjdGl2ZSR1bmlfcmFuayksCiAgICAgICAgICAgICByYWRpdXMgPSB+REVCVF9NRE4sCiAgICAgICAgICAgICBwb3B1cCA9IGNvbnRlbnQsCiAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAuNykgJT4lCiAgbGVhZmxldDo6YWRkTGVnZW5kKHBvc2l0aW9uID0gImJvdHRvbWxlZnQiLHBhbCA9IHBhbDEsIHZhbHVlcyA9IHNjX3RpbWVfMjAxOV9zZWxlY3RpdmUkdW5pX3JhbmssCiAgICAgICAgICAgIHRpdGxlID0gIkF2ZXJhZ2UgU3R1ZGVudCBMb2FucyAoUGVyIFN0dWRlbnQpIikKP2FkZExlZ2VuZApgYGAKYGBge3J9CnNldHdkKCd+L0Ryb3Bib3ggKEJ1c2luZXNzKS9TcHJpbmcgMjAyMS9RTVNTIDUwNjMgLSBEYXRhIFZpc3VhbGl6YXRpb24gL0dyb3VwX0dfSGlnaGVyRWQvJykKc2NfdGltZSA8LSByZWFkX2Nzdignc3JjLzIwMTBfMjAxOV9zdHVkZW50X2RlYnQuY3N2JykgCnNjX3RpbWUKYGBgCgpUaGUgY29kZSBjaHVuayBiZWxvdyBoYXMgbm90IGJlZW4gdG91Y2hlZCBhbmQgaW5jbHVkZXMgU2hpbnkgY29kZSBmb3IgbGF0ZXIgdXNlLiAKYGBge3J9CnNldHdkKCd+L0Ryb3Bib3ggKEJ1c2luZXNzKS9TcHJpbmcgMjAyMS9RTVNTIDUwNjMgLSBEYXRhIFZpc3VhbGl6YXRpb24gL0dyb3VwX0dfSGlnaGVyRWQvJykKc2NfdGltZSA8LSByZWFkX2Nzdignc3JjLzIwMTBfMjAxOV9zdHVkZW50X2RlYnQuY3N2JykgCgpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KHNoaW55KQp1aSA8LSBmbHVpZFBhZ2UoCiAgdGl0bGVQYW5lbCgiSSBsb3ZlIEdyYXBocyBhYm91dCBTdHVkZW50IERlYnQiKSwKICAjIENPREUgQkVMT1c6IEFkZCBzZWxlY3QgaW5wdXQgbmFtZWQgInNleCIgdG8gY2hvb3NlIGJldHdlZW4gIk0iIGFuZCAiRiIKICBzZWxlY3RJbnB1dCgneWVhcicsCiAgJ1llYXInLAogIGNob2ljZXM9YygyMDEwLDIwMTEsMjAxMiwyMDEzLDIwMTQsMjAxNSwyMDE2LDIwMTcsMjAxOCwyMDE5KSksIAogICMgQWRkIHBsb3Qgb3V0cHV0IHRvIGRpc3BsYXkgdG9wIDEwIG1vc3QgcG9wdWxhciBuYW1lcwogIGxlYWZsZXRPdXRwdXQoInN0dWRlbnRkZWJ0bWFwIikKKQoKc2VydmVyIDwtIGZ1bmN0aW9uKGlucHV0LCBvdXRwdXQsIHNlc3Npb24pewogICMgUmVuZGVyIHBsb3Qgb2YgdG9wIDEwIG1vc3QgcG9wdWxhciBuYW1lcwogICAgc2NfdGltZV9zZWxlY3RpdmUgPC0gcmVhY3RpdmUoewogICAgICBzY190aW1lICU+JSAKICAgICAgICBkcGx5cjo6ZmlsdGVyKFllYXJfRW5kaW5nID09IGlucHV0JHllYXIpICU+JSAKICAgICAgICBkcGx5cjo6ZmlsdGVyKERFQlRfTUROICE9J1ByaXZhY3lTdXBwcmVzc2VkJykgJT4lCiAgICAgICAgdHJhbnNmb3JtKERFQlRfTUROID0gYXMubnVtZXJpYyhERUJUX01ETikpICU+JSAKICAgICAgICBkcGx5cjo6bXV0YXRlKERFQlRfTUROID0gaWZlbHNlKGlzLm5hKERFQlRfTUROKSwgMCwgREVCVF9NRE4pKSAlPiUKICAgICAgICBtdXRhdGUoREVCVF9NRE5fU1RVREVOVCA9IERFQlRfTUROKlVHRFMpICU+JSAKICAgICAgICBncm91cF9ieShTVEFCQlIpICU+JSAKICAgICAgICBtdXRhdGUoYEF2ZXJhZ2UgU3R1ZGVudCBMb2Fuc2A9c3VtKERFQlRfTUROX1NUVURFTlQsbmEucm09VFJVRSkvc3VtKFVHRFMsbmEucm09VFJVRSkpICU+JSAKICAgICAgICBkcGx5cjo6cmVuYW1lKGxhdCA9IExBVElUVURFKSAlPiUgCiAgICAgICAgZHBseXI6OnJlbmFtZShsb25nID0gTE9OR0lUVURFKSAlPiUgCiAgICAgICAgZHBseXI6OnJlbmFtZShzdGF0ZSA9IFNUQUJCUikgJT4lCiAgICAgICAgZmlsdGVyKEFETV9SQVRFIDwgMC4zKSAlPiUgCiAgICAgICAgZHBseXI6Om11dGF0ZSh1bmlfcmFuayA9IGNhc2Vfd2hlbigKICAgICAgICAgIEFETV9SQVRFIDwgMC4wNSB+ICdlbGl0ZScsCiAgICAgICAgICBBRE1fUkFURSA8IDAuMiB+ICdoaWdobHkgc2VsZWN0aXZlJywKICAgICAgICAgIFRSVUUgfiAnc2VsZWN0aXZlJykpCiAgICAgIH0pCiAgICAKCiAgICAKICBvdXRwdXQkc3R1ZGVudGRlYnRtYXAgPC0gcmVuZGVyTGVhZmxldCh7CiAgICBjb250ZW50IDwtIHBhc3RlKAogICAgICAiU2Nob29sIixzY190aW1lX3NlbGVjdGl2ZSRJTlNUTk0sIjxici8+IiwKICAgICAgIk51bWJlciBvZiBVbmRlcmdyYWRzOiIsc2NfdGltZV9zZWxlY3RpdmUkVUdEUywiPGJyLz4iLAogICAgICAiU2VsZWN0aXZpdHk6Iiwgc2NfdGltZV9zZWxlY3RpdmUkdW5pX3JhbmssIjxici8+IiwKICAgICAgIk1lZGlhbiBEZWJ0OiIscGFzdGUoJyQnLHJvdW5kKHNjX3RpbWVfc2VsZWN0aXZlJERFQlRfTUROLDIpKSwiPGJyLz4iLAogICAgICAiWWVhciIsc2NfdGltZV9zZWxlY3RpdmUkYFllYXJfRW5kaW5nYCkKICAgIAogICAgbGVhZmxldCAoc2NfdGltZSkgJT4lIGFkZFRpbGVzKCdodHRwOi8ve3N9LmJhc2VtYXBzLmNhcnRvY2RuLmNvbS9kYXJrX2FsbC97en0ve3h9L3t5fS5wbmcnKSAlPiUKICAgICAgYWRkQ2lyY2xlcyhjb2wgPSB+cGFsMShzY190aW1lJHVuaV9yYW5rKSwKICAgICAgICAgICAgICAgICByYWRpdXMgPSB+REVCVF9NRE4sCiAgICAgICAgICAgICAgICAgcG9wdXAgPSBjb250ZW50LAogICAgICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMC43KSAlPiUKICAgICAgbGVhZmxldDo6YWRkTGVnZW5kKHBvc2l0aW9uID0gImJvdHRvbWxlZnQiLHBhbCA9IHBhbDEsIHZhbHVlcyA9IHNjX3RpbWUkdW5pX3JhbmssCiAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICJBdmVyYWdlIFN0dWRlbnQgTG9hbnMgKFBlciBTdHVkZW50KSIpCiAgICB9KQogICAgb2JzZXJ2ZSh7CiAgICBsZWFmbGV0UHJveHkoInN0dWRlbnRkZWJ0bWFwIiwgZGF0YSA9IHNjX3RpbWVfc2VsZWN0aXZlKCkpCiAgfSkgCn0KCnNoaW55QXBwKHVpID0gdWksIHNlcnZlciA9IHNlcnZlcikKCmBgYAoKYGBge3J9CnVpIDwtIGZsdWlkUGFnZSgKICB0aXRsZVBhbmVsKCdUaGUgQ29zdCBvZiBIaWdoZXIgRWR1Y2F0aW9uOiBBbiBFeHBsb3JhdGlvbiBvZiBTdHVkZW50IERlYnQgaW4gQW1lcmljYW4gVW5pdmVyc2l0aWVzJykKICAKKQogCmBgYAoK